package org.libermundi.theorcs.core.tapestry.services.assets;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.internal.services.AssetResourceLocator;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.services.AssetFactory;
import org.apache.tapestry5.upload.services.UploadedFile;

public class OfsFileManager implements FileManager {
	private String _baseDirPath;
	
	private AssetResourceLocator _resourceLocator;
	
	private AssetFactory _assetFactory;
	
	private FileValidator _validator;
	
	private ImageCache _imageCache;

	private String _subDirName;
	
	public OfsFileManager(String homeDirPath, String subDirName, AssetResourceLocator resourceLocator,
			FileValidator fileValidator, ImageCache imageCache, AssetFactory assetFactory) {
		this._baseDirPath = OfsFilesUtils.computerBaseFilerPath(homeDirPath,subDirName);
		this._subDirName = subDirName;
		this._resourceLocator = resourceLocator;
		this._validator = fileValidator;
		this._imageCache = imageCache;
		this._assetFactory = assetFactory;
	}
	
	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#getBasePath()
	 */
	@Override
	public String getBasePath() {
		return this._baseDirPath;
	}
	/*
	 * (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#save(org.apache.tapestry5.upload.services.UploadedFile)
	 */
	@Override
	public Asset save(UploadedFile uploadedFile){
		return save(uploadedFile, Boolean.TRUE);
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#save(org.apache.tapestry5.upload.services.UploadedFile, boolean)
	 */
	@Override
	public Asset save(UploadedFile uploadedFile, boolean makeUnique) {
		return save(uploadedFile, makeUnique, FileType.ANY);
	}

	/*
	 * (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#save(org.apache.tapestry5.upload.services.UploadedFile, org.libermundi.theorcs.core.tapestry.services.assets.FileManager.FileType)
	 */
	@Override
	public Asset save(UploadedFile uploadedFile, FileType fileType) {
		return save(uploadedFile, Boolean.TRUE, fileType);
	}
	
		/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#save(org.apache.tapestry5.upload.services.UploadedFile, java.io.File, org.libermundi.theorcs.core.tapestry.services.assets.FileType)
	 */
	@Override
	public Asset save(UploadedFile uploadedFile, File targetDir, FileType fileType) {
		if(!_validator.isAllowed(uploadedFile.getFileName(), fileType)){
			throw new RuntimeException("You are not allowed to Upload files with extension : " + FilenameUtils.getExtension(uploadedFile.getFileName()));
		}
		String targetFileName = OfsFilesUtils.cleanFileName(uploadedFile.getFileName(),Boolean.FALSE);
		String fullPath = OfsFilesUtils.computeFullStoreFilePath(targetDir.getAbsolutePath(), targetFileName);
		OfsFilesUtils.sanitiseDirPath(FilenameUtils.getFullPath(fullPath));
		File targetFile = new File(fullPath);
		uploadedFile.write(targetFile);

		String partialPath = _subDirName  + OfsFilesUtils.FILE_SEPARATOR + OfsFilesUtils.computePartialStoreFilePath(targetFileName);
		
		return getAsset(partialPath);
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#save(org.apache.tapestry5.upload.services.UploadedFile, boolean, org.libermundi.theorcs.core.tapestry.services.assets.FileType)
	 */
	@Override
	public Asset save(UploadedFile uploadedFile, boolean makeUnique, FileType fileType) {
		if(!_validator.isAllowed(uploadedFile.getFileName(), fileType)){
			throw new RuntimeException("You are not allowed to Upload files with extension : " + FilenameUtils.getExtension(uploadedFile.getFileName()));
		}
		String targetFileName = OfsFilesUtils.cleanFileName(uploadedFile.getFileName(),makeUnique);
		String fullPath = OfsFilesUtils.computeFullStoreFilePath(getBasePath(), targetFileName);
		OfsFilesUtils.sanitiseDirPath(FilenameUtils.getFullPath(fullPath));
		File targetFile = new File(fullPath);
		uploadedFile.write(targetFile);

		String partialPath = _subDirName  + OfsFilesUtils.FILE_SEPARATOR + OfsFilesUtils.computePartialStoreFilePath(targetFileName);
		
		return getAsset(partialPath);
	}
	
	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#delete(java.lang.String)
	 */
	@Override
	public void delete(String relativePath) {
		File fileToDelete = getFileFromRelativePath(relativePath); 
		delete(fileToDelete);
	}
	
	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#delete(java.io.File)
	 */
	@Override
	public void delete(File fileToDelete) {
		FileUtils.deleteQuietly(fileToDelete);
		
		_imageCache.removeFromCache(fileToDelete);
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#exists(java.lang.String)
	 */
	@Override
	public boolean exists(String relativePath) {
		File fileToCheck = getFileFromRelativePath(relativePath);
		if(fileToCheck.exists() && fileToCheck.isFile()) {
			return Boolean.TRUE;
		}
		
		return Boolean.FALSE;
	}
	
	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#isSpecialDir(java.io.File)
	 */
	@Override
	public boolean isSpecialDir(File file) {
		String fileName = file.getName();
		return (".".equals(fileName) || "..".equals(fileName));
	}
	
	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#getAsset(java.lang.String)
	 */
	@Override
	public Asset getAsset(String relativePath) {
		try {
			Resource r = _resourceLocator.findClasspathResourceForPath(relativePath);
			return _assetFactory.createAsset(r);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#getAsset(java.io.File)
	 */
	@Override
	public Asset getAsset(File file) {
		File basePath = new File(getBasePath());
		String relativePath = file.getAbsolutePath().replace(basePath.getAbsolutePath() , "");
		return getAsset(_subDirName  + OfsFilesUtils.FILE_SEPARATOR + relativePath);
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#rename(java.io.File, java.io.File)
	 */
	@Override
	public void rename(File targetFile, File futureFile) throws IOException {
		if(!targetFile.renameTo(futureFile)){
			throw new IOException("Could not rename File : " + targetFile.getName() + " to : " + futureFile.getName());
		}
		
		_imageCache.removeFromCache(targetFile);
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#copy(java.io.File, java.io.File)
	 */
	@Override
	public void copy(File fileTarget, File futureFile) throws IOException {
		try {
			if (fileTarget.isDirectory()) {
				FileUtils.copyDirectory(fileTarget, futureFile);
			} else {
				FileUtils.copyFile(fileTarget, futureFile);
			}
		} catch (IOException e) {
			throw new IOException("Unable to copy file from " + fileTarget.getPath() + " to " + futureFile.getPath());
		}
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#createFile(java.io.File, java.lang.Object)
	 */
	@Override
	public void createFile(File newFile, ByteArrayOutputStream os) throws IOException {
		if (os == null) {
			os = new ByteArrayOutputStream();
		}
		try {
			if (!newFile.createNewFile()) {
				throw new IOException("unable to create file");
			}
			try {
				FileOutputStream fs = new FileOutputStream(newFile);
				fs.write(os.toByteArray());
				fs.flush();
				fs.close();
			} catch (Exception ee) {
				newFile.delete();
				throw new IOException("Unable to write file");
			}
		} catch (Exception e) {
			throw new IOException("Unable to create file");
		}
	}

	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#createFolder(java.io.File)
	 */
	@Override
	public void createFolder(File newDir) {
		if(!newDir.exists()){
			newDir.mkdirs();
		}
	}
	
	/* (non-Javadoc)
	 * @see org.libermundi.theorcs.core.tapestry.services.assets.FileManager#getFileSize(java.io.File)
	 */
	@Override
	public long getFileSize(File file) {
		return file.length();
	}

	@Override
	public long getDirSize(File dir) {
		return FileUtils.sizeOfDirectory(dir);
	}
	
	private String stripRelativePath(String relativePath){
		return relativePath.substring(_subDirName.length() + 1); //We should remove the subDirName + the following "/"
	}
	
	private File getFileFromRelativePath(String relativePath){
		return new File(getBasePath() + OfsFilesUtils.FILE_SEPARATOR + stripRelativePath(relativePath));
	}
}
